library ieee;
use ieee.std_logic_1164.all;
use IEEE.NUMERIC_STD.all;
use work.typeDefinitions.all;

entity StallLogic is
  port
    (
      nReset       : in  std_logic;
      icacheWait   : in  std_logic;
      dcacheWait   : in  std_logic;
      hold         : out stageHoldSignals;
      invalidate   : out stageInvalidateSignals;
      instructions : in  interStageOperation;
      forwardIn    : in  forwardSignalsOut
      );
end StallLogic;

architecture StallLogic_arch of StallLogic is
  signal resetHold, cacheHold, loaduseHold, loadbranchHold, usebranchHold : stageHoldSignals;
  signal cacheInv, loaduseInv, loadbranchInv, usebranchInv                : stageInvalidateSignals;
  signal t_hold                                                           : stageHoldSignals;
  signal t_inv                                                            : stageInvalidateSignals;
begin

  t_hold.pc    <= resetHold.pc or cacheHold.pc or loaduseHold.pc or loadbranchHold.pc or usebranchHold.pc;
  t_hold.fetch <= resetHold.fetch or cacheHold.fetch or loaduseHold.fetch or loadbranchHold.fetch or usebranchHold.fetch;
  t_hold.id    <= resetHold.id or cacheHold.id or loaduseHold.id or loadbranchHold.id or usebranchHold.id;
  t_hold.exec  <= resetHold.exec or cacheHold.exec or loaduseHold.exec or loadbranchHold.exec or usebranchHold.exec;
  t_hold.mem   <= resetHold.mem or cacheHold.mem or loaduseHold.mem or loadbranchHold.mem or usebranchHold.mem;
  t_hold.wb    <= resetHold.wb or cacheHold.wb or loaduseHold.wb or loadbranchHold.wb or usebranchHold.wb;

  t_inv.fetch <= cacheInv.fetch or loaduseInv.fetch or loadbranchInv.fetch or usebranchInv.fetch;
  t_inv.id    <= cacheInv.id or loaduseInv.id or loadbranchInv.id or usebranchInv.id;
  t_inv.exec  <= cacheInv.exec or loaduseInv.exec or loadbranchInv.exec or usebranchInv.exec;
  t_inv.mem   <= cacheInv.mem or loaduseInv.mem or loadbranchInv.mem or usebranchInv.mem;
  t_inv.wb    <= cacheInv.wb or loaduseInv.wb or loadbranchInv.wb or usebranchInv.wb;

  hold <= t_hold;

  invalidate.fetch <= t_inv.fetch and not t_hold.id;
  invalidate.id    <= t_inv.id and not t_hold.exec;
  invalidate.exec  <= t_inv.exec and not t_hold.mem;
  invalidate.mem   <= t_inv.mem;
  invalidate.wb    <= t_inv.wb;

  -- reset holds
  process(nReset)
  begin
    resetHold <= ('0', '0', '0', '0', '0', '0');
    if(nReset = '0') then
      resetHold.pc    <= '1';
      resetHold.fetch <= '1';
      resetHold.id    <= '1';
      resetHold.exec  <= '1';
      resetHold.mem   <= '1';
    end if;
  end process;

  -- icache/dcache holds
  process(icacheWait, dcacheWait, instructions)
  begin
    cacheHold <= ('0', '0', '0', '0', '0', '0');
    cacheInv  <= ('0', '0', '0', '0', '0');
    if(dcacheWait = '1') then
      cacheHold <= ('1', '1', '1', '1', '1', '1');
    elsif( icacheWait = '1' and dcacheWait = '0') then
      cacheHold.pc    <= '1';
      cacheHold.fetch <= '1';
      cacheInv.fetch  <= '1';
      if( instructions.decode = o_jal or
				instructions.decode = o_beq or
				instructions.decode = o_bne or
				instructions.decode = o_j or
				instructions.decode = o_jr ) then
      		cacheInv.fetch <= '0';
      		cacheHold.id <= '1';
      		cacheInv.id <= '1';
      end if;
      		
    end if;
  end process;

  -- load branch hazards
  process(instructions, forwardIn)
  begin
    loadbranchHold <= ('0', '0', '0', '0', '0', '0');
    loadbranchInv  <= ('0', '0', '0', '0', '0');
    if(instructions.decode = o_beq or instructions.decode = o_bne) then
      if(instructions.exec = o_lw or instructions.exec = o_ll) then
        if((forwardIn.id.rt = forwardIn.mem.rt and forwardIn.id.rt /= "00000") or (forwardIn.id.rs = forwardIn.mem.rt and forwardIn.id.rs /= "00000")) then
          loadbranchHold.pc    <= '1';
          loadbranchHold.fetch <= '1';
          loadbranchHold.id    <= '1';
          loadbranchHold.exec  <= '1';
          loadbranchInv.exec   <= '1';
        end if;
      elsif((forwardIn.id.rs = forwardIn.exec.destination and forwardIn.id.rs /= "00000") or (forwardIn.id.rt = forwardIn.exec.destination and forwardIn.id.rt /= "00000")) then
        loadbranchHold.pc    <= '1';
        loadbranchHold.fetch <= '1';
        loadbranchHold.id    <= '1';
        loadbranchInv.id     <= '1';
      end if;
    elsif(instructions.decode = o_jr) then
      if(instructions.id = o_lw or instructions.id = o_ll) then
        if(forwardIn.id.rs = forwardIn.exec.rt and forwardIn.id.rs /= "00000") then
          loadbranchHold.pc    <= '1';
          loadbranchHold.fetch <= '1';
          loadbranchHold.id    <= '1';
          loadbranchInv.id     <= '1';
        end if;
      elsif(instructions.exec = o_lw or instructions.exec = o_ll ) then
        if(forwardIn.id.rs = forwardIn.mem.rt and forwardIn.id.rs /= "00000") then
          loadbranchHold.pc    <= '1';
          loadbranchHold.fetch <= '1';
          loadbranchHold.id    <= '1';
          loadbranchHold.exec  <= '1';
          loadbranchInv.exec   <= '1';
        end if;
      end if;
    end if;
  end process;

  -- branch use hazards
  process(instructions, forwardIn)
  begin
    usebranchhold <= ('0', '0', '0', '0', '0', '0');
    usebranchInv  <= ('0', '0', '0', '0', '0');
    if(forwardIn.id.npcSel /= pc_add4) then
      usebranchInv.fetch <= '1';
    end if;
  end process;

  -- load use hazards
  process(instructions, forwardIn)
  begin
    loaduseHold <= ('0', '0', '0', '0', '0', '0');
    loaduseInv  <= ('0', '0', '0', '0', '0');
    if(instructions.exec = o_lw or instructions.exec = o_ll) then
      if(forwardIn.mem.rt = forwardIn.exec.rs and forwardIn.exec.rs /= "00000") then
        case instructions.id is
                                        -- all instructions that require rs
          when o_addu | o_and | o_nor | o_or | o_slt | o_sltu | o_sll | o_srl | o_subu | o_xor | o_addiu | o_andi | o_lw | o_ori | o_slti | o_sltiu | o_sw | o_xori | o_sc | o_ll =>
            loaduseHold.pc    <= '1';
            loaduseHold.fetch <= '1';
            loaduseHold.id    <= '1';
            loaduseHold.exec  <= '1';
            loaduseInv.exec   <= '1';
          when others =>
                                        -- nothing
        end case;
      end if;
      if(forwardIn.mem.rt = forwardIn.exec.rt and forwardIn.exec.rs /= "00000") then
        case instructions.id is
                                        -- all instructions that require rt
          when o_addu | o_and | o_nor | o_or | o_slt | o_sltu | o_subu | o_xor | o_sw | o_sc =>
            loaduseHold.pc    <= '1';
            loaduseHold.fetch <= '1';
            loaduseHold.id    <= '1';
            loaduseHold.exec  <= '1';
            loaduseInv.exec   <= '1';
          when others =>
                                        -- nothing
        end case;
      end if;
    end if;
  end process;

end StallLogic_arch;
